/*
 * DotNetCrypt - an open source library of cryptographic algorithms for .NET
 * Copyright (C) 2009 David Musgrove
 * 
 * This file is part of DotNetCrypt.
 *
 * DotNetCrypt is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * DotNetCrypt is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;

namespace DotNetCrypt
{
    /// <summary>
    /// Performs a cryptographic transformation of data using the
    /// <see cref="Blowfish" /> algorithm. This class cannot be inherited.
    /// </summary>
    public sealed class TwofishManagedTransform : ManagedTransformBase
    {
        private const uint MDS_PRIMITIVE_SHR1 = 0xb4;
        private const uint MDS_PRIMITIVE_SHR2 = 0x5a;
        private const uint MDS_PRIMITIVE_SHR1_XOR_SHR2 = 0xee;
        private const uint RS_PRIMITIVE = 0x14d;

        private static readonly uint[] _q0 = { 0xa9, 0x67, 0xb3, 0xe8, 0x04, 0xfd, 0xa3, 0x76, 0x9a, 0x92, 0x80, 0x78, 0xe4, 0xdd, 0xd1, 0x38, 0x0d, 0xc6, 0x35, 0x98, 0x18, 0xf7, 0xec, 0x6c, 0x43, 0x75, 0x37, 0x26, 0xfa, 0x13, 0x94, 0x48, 0xf2, 0xd0, 0x8b, 0x30, 0x84, 0x54, 0xdf, 0x23, 0x19, 0x5b, 0x3d, 0x59, 0xf3, 0xae, 0xa2, 0x82, 0x63, 0x01, 0x83, 0x2e, 0xd9, 0x51, 0x9b, 0x7c, 0xa6, 0xeb, 0xa5, 0xbe, 0x16, 0x0c, 0xe3, 0x61, 0xc0, 0x8c, 0x3a, 0xf5, 0x73, 0x2c, 0x25, 0x0b, 0xbb, 0x4e, 0x89, 0x6b, 0x53, 0x6a, 0xb4, 0xf1, 0xe1, 0xe6, 0xbd, 0x45, 0xe2, 0xf4, 0xb6, 0x66, 0xcc, 0x95, 0x03, 0x56, 0xd4, 0x1c, 0x1e, 0xd7, 0xfb, 0xc3, 0x8e, 0xb5, 0xe9, 0xcf, 0xbf, 0xba, 0xea, 0x77, 0x39, 0xaf, 0x33, 0xc9, 0x62, 0x71, 0x81, 0x79, 0x09, 0xad, 0x24, 0xcd, 0xf9, 0xd8, 0xe5, 0xc5, 0xb9, 0x4d, 0x44, 0x08, 0x86, 0xe7, 0xa1, 0x1d, 0xaa, 0xed, 0x06, 0x70, 0xb2, 0xd2, 0x41, 0x7b, 0xa0, 0x11, 0x31, 0xc2, 0x27, 0x90, 0x20, 0xf6, 0x60, 0xff, 0x96, 0x5c, 0xb1, 0xab, 0x9e, 0x9c, 0x52, 0x1b, 0x5f, 0x93, 0x0a, 0xef, 0x91, 0x85, 0x49, 0xee, 0x2d, 0x4f, 0x8f, 0x3b, 0x47, 0x87, 0x6d, 0x46, 0xd6, 0x3e, 0x69, 0x64, 0x2a, 0xce, 0xcb, 0x2f, 0xfc, 0x97, 0x05, 0x7a, 0xac, 0x7f, 0xd5, 0x1a, 0x4b, 0x0e, 0xa7, 0x5a, 0x28, 0x14, 0x3f, 0x29, 0x88, 0x3c, 0x4c, 0x02, 0xb8, 0xda, 0xb0, 0x17, 0x55, 0x1f, 0x8a, 0x7d, 0x57, 0xc7, 0x8d, 0x74, 0xb7, 0xc4, 0x9f, 0x72, 0x7e, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, 0x6e, 0x50, 0xde, 0x68, 0x65, 0xbc, 0xdb, 0xf8, 0xc8, 0xa8, 0x2b, 0x40, 0xdc, 0xfe, 0x32, 0xa4, 0xca, 0x10, 0x21, 0xf0, 0xd3, 0x5d, 0x0f, 0x00, 0x6f, 0x9d, 0x36, 0x42, 0x4a, 0x5e, 0xc1, 0xe0 };
        private static readonly uint[] _q1 = { 0x75, 0xf3, 0xc6, 0xf4, 0xdb, 0x7b, 0xfb, 0xc8, 0x4a, 0xd3, 0xe6, 0x6b, 0x45, 0x7d, 0xe8, 0x4b, 0xd6, 0x32, 0xd8, 0xfd, 0x37, 0x71, 0xf1, 0xe1, 0x30, 0x0f, 0xf8, 0x1b, 0x87, 0xfa, 0x06, 0x3f, 0x5e, 0xba, 0xae, 0x5b, 0x8a, 0x00, 0xbc, 0x9d, 0x6d, 0xc1, 0xb1, 0x0e, 0x80, 0x5d, 0xd2, 0xd5, 0xa0, 0x84, 0x07, 0x14, 0xb5, 0x90, 0x2c, 0xa3, 0xb2, 0x73, 0x4c, 0x54, 0x92, 0x74, 0x36, 0x51, 0x38, 0xb0, 0xbd, 0x5a, 0xfc, 0x60, 0x62, 0x96, 0x6c, 0x42, 0xf7, 0x10, 0x7c, 0x28, 0x27, 0x8c, 0x13, 0x95, 0x9c, 0xc7, 0x24, 0x46, 0x3b, 0x70, 0xca, 0xe3, 0x85, 0xcb, 0x11, 0xd0, 0x93, 0xb8, 0xa6, 0x83, 0x20, 0xff, 0x9f, 0x77, 0xc3, 0xcc, 0x03, 0x6f, 0x08, 0xbf, 0x40, 0xe7, 0x2b, 0xe2, 0x79, 0x0c, 0xaa, 0x82, 0x41, 0x3a, 0xea, 0xb9, 0xe4, 0x9a, 0xa4, 0x97, 0x7e, 0xda, 0x7a, 0x17, 0x66, 0x94, 0xa1, 0x1d, 0x3d, 0xf0, 0xde, 0xb3, 0x0b, 0x72, 0xa7, 0x1c, 0xef, 0xd1, 0x53, 0x3e, 0x8f, 0x33, 0x26, 0x5f, 0xec, 0x76, 0x2a, 0x49, 0x81, 0x88, 0xee, 0x21, 0xc4, 0x1a, 0xeb, 0xd9, 0xc5, 0x39, 0x99, 0xcd, 0xad, 0x31, 0x8b, 0x01, 0x18, 0x23, 0xdd, 0x1f, 0x4e, 0x2d, 0xf9, 0x48, 0x4f, 0xf2, 0x65, 0x8e, 0x78, 0x5c, 0x58, 0x19, 0x8d, 0xe5, 0x98, 0x57, 0x67, 0x7f, 0x05, 0x64, 0xaf, 0x63, 0xb6, 0xfe, 0xf5, 0xb7, 0x3c, 0xa5, 0xce, 0xe9, 0x68, 0x44, 0xe0, 0x4d, 0x43, 0x69, 0x29, 0x2e, 0xac, 0x15, 0x59, 0xa8, 0x0a, 0x9e, 0x6e, 0x47, 0xdf, 0x34, 0x35, 0x6a, 0xcf, 0xdc, 0x22, 0xc9, 0xc0, 0x9b, 0x89, 0xd4, 0xed, 0xab, 0x12, 0xa2, 0x0d, 0x52, 0xbb, 0x02, 0x2f, 0xa9, 0xd7, 0x61, 0x1e, 0xb4, 0x50, 0x04, 0xf6, 0xc2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xbe, 0x91 };

        private readonly int _keyLength;
        private uint[] _keySchedule;
        private uint _s00, _s01, _s02, _s03, _s10, _s11, _s12, _s13, _s20, _s21, _s22, _s23, _s30, _s31, _s32, _s33;
        
        internal TwofishManagedTransform(ISymmetricAlgorithm algorithm, byte[] rgbKey, byte[] rgbIv, TransformDirection transformDirection)
            : base(algorithm, rgbIv, transformDirection, Endianness.Little)
        {
            Mode = algorithm.ExtendedMode;
            _keyLength = rgbKey.Length / 8;
            CalculateKeySchedule(rgbKey);
        }

        private void CalculateKeySchedule(byte[] rgbKey)
        {
            var mEven = new uint[_keyLength];
            var mOdd = new uint[_keyLength];
            var tempState = new uint[_keyLength];
            if (_keySchedule == null)
            {
                _keySchedule = new uint[40];
            }

            int index = 0;
            for (int i = 0; i < _keyLength; i++)
            {
                uint m0 = rgbKey[index++];
                uint m1 = rgbKey[index++];
                uint m2 = rgbKey[index++];
                uint m3 = rgbKey[index++];
                uint m4 = rgbKey[index++];
                uint m5 = rgbKey[index++];
                uint m6 = rgbKey[index++];
                uint m7 = rgbKey[index++];
                mEven[i] = m0 | (m1 << 8) | (m2 << 16) | (m3 << 24);
                mOdd[i] = m4 | (m5 << 8) | (m6 << 16) | (m7 << 24);
                tempState[_keyLength - i - 1] = ((MultiplyByRS(m0, 0x01) ^ MultiplyByRS(m1, 0xa4) ^ MultiplyByRS(m2, 0x55) ^ MultiplyByRS(m3, 0x87) ^
                    MultiplyByRS(m4, 0x5a) ^ MultiplyByRS(m5, 0x58) ^ MultiplyByRS(m6, 0xdb) ^ MultiplyByRS(m7, 0x9e))) |
                    ((MultiplyByRS(m0, 0xa4) ^ MultiplyByRS(m1, 0x56) ^ MultiplyByRS(m2, 0x82) ^ MultiplyByRS(m3, 0xf3) ^
                    MultiplyByRS(m4, 0x1e) ^ MultiplyByRS(m5, 0xc6) ^ MultiplyByRS(m6, 0x68) ^ MultiplyByRS(m7, 0xe5)) << 8) |
                    ((MultiplyByRS(m0, 0x02) ^ MultiplyByRS(m1, 0xa1) ^ MultiplyByRS(m2, 0xfc) ^ MultiplyByRS(m3, 0xc1) ^
                    MultiplyByRS(m4, 0x47) ^ MultiplyByRS(m5, 0xae) ^ MultiplyByRS(m6, 0x3d) ^ MultiplyByRS(m7, 0x19)) << 16) |
                    ((MultiplyByRS(m0, 0xa4) ^ MultiplyByRS(m1, 0x55) ^ MultiplyByRS(m2, 0x87) ^ MultiplyByRS(m3, 0x5a) ^
                    MultiplyByRS(m4, 0x58) ^ MultiplyByRS(m5, 0xdb) ^ MultiplyByRS(m6, 0x9e) ^ MultiplyByRS(m7, 0x03)) << 24);
            }
            uint temp = tempState[0]; _s00 = temp & 0xff; _s01 = (temp >> 8) & 0xff; _s02 = (temp >> 16) & 0xff; _s03 = temp >> 24;
            temp = tempState[1]; _s10 = temp & 0xff; _s11 = (temp >> 8) & 0xff; _s12 = (temp >> 16) & 0xff; _s13 = temp >> 24;
            if (_keyLength >= 3) { temp = tempState[2]; _s20 = temp & 0xff; _s21 = (temp >> 8) & 0xff; _s22 = (temp >> 16) & 0xff; _s23 = temp >> 24; }
            if (_keyLength == 4) { temp = tempState[3]; _s30 = temp & 0xff; _s31 = (temp >> 8) & 0xff; _s32 = (temp >> 16) & 0xff; _s33 = temp >> 24; }

            // Now loop through calculating the word values in the expanded key.
            uint rhoMultiple = 0;
            index = 0;
            uint a = 0;
            uint b = 0;
            for (uint i = 0; i < 20; i++)
            {
                FunctionH(rhoMultiple, mEven, ref a);
                rhoMultiple += 0x01010101;
                FunctionH(rhoMultiple, mOdd, ref b);
                rhoMultiple += 0x01010101;
                b = ((b << 8) | (b >> 24));
                temp = a + b;
                _keySchedule[index++] = temp;
                temp += b;
                _keySchedule[index++] = ((temp << 9) | (temp >> 23));
            }
        }

        /// <summary>
        /// Performs the encryption transformation on a block of bytes that
        /// have been translated into words using the little endian convention.
        /// </summary>
        /// <param name="plain">
        /// The words to encrypt.
        /// </param>
        [CLSCompliant(false)]
        protected internal override void Encrypt(uint[] plain)
        {
            // Perform input whitening.
            uint r0 = plain[0] ^ _keySchedule[0];
            uint r1 = plain[1] ^ _keySchedule[1];
            uint r2 = plain[2] ^ _keySchedule[2];
            uint r3 = plain[3] ^ _keySchedule[3];
            uint t0 = 0;

            // Carry out the sixteen rounds.
            for (int i = 0; i < 16; i++)
            {

                // For performance, function F is expanded rather than called
                // as a separate function.
                FunctionG(r0, ref t0);
                uint t1 = r1 << 8 | r1 >> 24;
                FunctionG(t1, ref t1);

                // Mix and swap the output with the other two input words.
                uint T = r0;
                r0 = r2 ^ (t0 + t1 + _keySchedule[(i << 1) + 8]);
                r0 = r0 >> 1 | r0 << 31;
                r2 = T;
                T = r1;
                r1 = r3 << 1 | r3 >> 31;
                r1 ^= (t0 + 2 * t1 + _keySchedule[(i << 1) + 9]);
                r3 = T;
            }

            // Perform the swap and output whitening.
            plain[0] = r2 ^ _keySchedule[4];
            plain[1] = r3 ^ _keySchedule[5];
            plain[2] = r0 ^ _keySchedule[6];
            plain[3] = r1 ^ _keySchedule[7];
        }

        /// <summary>
        /// Performs the decryption transformation on a block of bytes that
        /// have been translated into words using the little endian convention.
        /// </summary>
        /// <param name="cipher">
        /// The words to decrypt.
        /// </param>
        [CLSCompliant(false)]
        protected internal override void Decrypt(uint[] cipher)
        {
            uint r0 = cipher[0] ^ _keySchedule[4];
            uint r1 = cipher[1] ^ _keySchedule[5];
            uint r2 = cipher[2] ^ _keySchedule[6];
            uint r3 = cipher[3] ^ _keySchedule[7];
            uint t0 = 0;

            for (int i = 15; i >= 0; i--)
            {
                // For performance, function F is expanded rather than called
                // as a separate function.
                FunctionG(r0, ref t0);
                uint t1 = r1 << 8 | r1 >> 24;
                FunctionG(t1, ref t1);

                // Mix and swap the output with the other two input words.
                r2 = r2 << 1 | r2 >> 31;
                r2 ^= (t0 + t1 + _keySchedule[(i << 1) + 8]);
                r3 ^= (t0 + 2 * t1 + _keySchedule[(i << 1) + 9]);
                r3 = r3 >> 1 | r3 << 31;
                uint t = r0;
                r0 = r2;
                r2 = t;
                t = r1;
                r1 = r3;
                r3 = t;
            }

            // Perform the swap and reverse input whitening.
            cipher[0] = r2 ^ _keySchedule[0];
            cipher[1] = r3 ^ _keySchedule[1];
            cipher[2] = r0 ^ _keySchedule[2];
            cipher[3] = r1 ^ _keySchedule[3];
        }

        private void FunctionH(uint x, uint[] l, ref uint result)
        {
            // Split x into bytes.
            uint y0 = x & 0xff;
            uint y1 = (x >> 8) & 0xff;
            uint y2 = (x >> 16) & 0xff;
            uint y3 = x >> 24;

            // If l is 4 words long, then we grab the fourth word and perform
            // the relevant S-box and XOR calculations.
            if (_keyLength == 4)
            {
                y0 = (_q1[y0] ^ l[3]) & 0xff;
                y1 = (_q0[y1] ^ (l[3] >> 8)) & 0xff;
                y2 = (_q0[y2] ^ (l[3] >> 16)) & 0xff;
                y3 = _q1[y3] ^ (l[3] >> 24);
            }

            // If l is 3 or 4 words long, then we grab the third word and
            // perform the relevant S-box and XOR calculations.
            if (_keyLength >= 3)
            {
                y0 = (_q1[y0] ^ (l[2] & 0xff));
                y1 = (_q1[y1] ^ ((l[2] >> 8) & 0xff));
                y2 = (_q0[y2] ^ ((l[2] >> 16) & 0xff));
                y3 = _q0[y3] ^ (l[2] >> 24);
            }

            // Now we do the other two stages of FunctionH, which aleays run.
            y0 = _q1[_q0[_q0[y0] ^ (l[1] & 0xff)] ^ (l[0] & 0xff)];
            y1 = _q0[_q0[_q1[y1] ^ ((l[1] >> 8) & 0xff)] ^ ((l[0] >> 8) & 0xff)];
            y2 = _q1[_q1[_q0[y2] ^ ((l[1] >> 16) & 0xff)] ^ ((l[0] >> 16) & 0xff)];
            y3 = _q0[_q1[_q1[y3] ^ (l[1] >> 24)] ^ (l[0] >> 24)];

            // Finally, we multiple by the MDS matrix - as this has only three
            // distinct values for its entries, one of which is 0x01, we have
            // two optimised functions to perform specific multiplications.
            // Precalculate multiples for matrix that are used more than once.
            uint y02 = MultiplyByEF(y0);
            uint y12 = MultiplyByEF(y1);
            uint y22 = MultiplyByEF(y2);
            uint y31 = MultiplyBy5B(y3);

            result = (y0 ^ y12 ^ MultiplyBy5B(y2) ^ y31) |
                (MultiplyBy5B(y0) ^ y12 ^ y22 ^ y3) << 8 |
                (y02 ^ MultiplyBy5B(y1) ^ y2 ^ MultiplyByEF(y3)) << 16 |
                (y02 ^ y1 ^ y22 ^ y31) << 24;
        }

        private void FunctionG(uint x, ref uint result)
        {
            // Split X into bytes.
            uint y0 = x & 0xff;
            uint y1 = (x >> 8) & 0xff;
            uint y2 = (x >> 16) & 0xff;
            uint y3 = x >> 24;

            // If S is 4 words long, then we grab the fourth word and perform
            // the relevant S-box and XOR calculations.
            if (_keyLength == 4)
            {
                y0 = _q1[y0] ^ _s30;
                y1 = _q0[y1] ^ _s31;
                y2 = _q0[y2] ^ _s32;
                y3 = _q1[y3] ^ _s33;
            }

            // If S is 3 or 4 words long, then we grab the third word and
            // perform the relevant S-box and XOR calculations.
            if (_keyLength >= 3)
            {
                y0 = _q1[y0] ^ _s20;
                y1 = _q1[y1] ^ _s21;
                y2 = _q0[y2] ^ _s22;
                y3 = _q0[y3] ^ _s23;
            }

            // Now we do the other two stages of FunctionG, which aleays run.
            y0 = _q1[_q0[_q0[y0] ^ _s10] ^ _s00];
            y1 = _q0[_q0[_q1[y1] ^ _s11] ^ _s01];
            y2 = _q1[_q1[_q0[y2] ^ _s12] ^ _s02];
            y3 = _q0[_q1[_q1[y3] ^ _s13] ^ _s03];

            // Finally, we multiple by the MDS matrix - as this has only three
            // distinct values for its entries, one of which is 0x01, we have
            // two optimised functions to perform specific multiplications.
            // Precalculate multiples for matrix that are used more than once.
            uint y02 = MultiplyByEF(y0);
            uint y12 = MultiplyByEF(y1);
            uint y22 = MultiplyByEF(y2);
            uint y31 = MultiplyBy5B(y3);

            result = (y0 ^ y12 ^ MultiplyBy5B(y2) ^ y31) |
                (MultiplyBy5B(y0) ^ y12 ^ y22 ^ y3) << 8 |
                (y02 ^ MultiplyBy5B(y1) ^ y2 ^ MultiplyByEF(y3)) << 16 |
                (y02 ^ y1 ^ y22 ^ y31) << 24;
        }

        static private uint MultiplyBy5B(uint a)
        {
            uint c = a ^ (a >> 2);
            if (!((a & 0x02) == 0)) c ^= MDS_PRIMITIVE_SHR1;
            if (!((a & 0x01) == 0)) c ^= MDS_PRIMITIVE_SHR2;
            return c;
        }

// ReSharper disable InconsistentNaming
        static private uint MultiplyByEF(uint a)
// ReSharper restore InconsistentNaming
        {
            uint c = a ^ (a >> 2) ^ (a >> 1);
            if (!((a & 0x02) == 0)) c ^= MDS_PRIMITIVE_SHR1;
            if (!((a & 0x01) == 0)) c ^= MDS_PRIMITIVE_SHR1_XOR_SHR2;
            return c;
        }

// ReSharper disable InconsistentNaming
        static private uint MultiplyByRS(uint a, uint b)
// ReSharper restore InconsistentNaming
        {
            uint c = 0;
            if ((b & 0x80) != 0) c ^= (a << 7);
            if ((b & 0x40) != 0) c ^= (a << 6);
            if ((b & 0x20) != 0) c ^= (a << 5);
            if ((b & 0x10) != 0) c ^= (a << 4);
            if ((b & 0x08) != 0) c ^= (a << 3);
            if ((b & 0x04) != 0) c ^= (a << 2);
            if ((b & 0x02) != 0) c ^= (a << 1);
            if ((b & 0x01) != 0) c ^= a;
            if ((c & 0x8000) != 0) c ^= (RS_PRIMITIVE << 7);
            if ((c & 0x4000) != 0) c ^= (RS_PRIMITIVE << 6);
            if ((c & 0x2000) != 0) c ^= (RS_PRIMITIVE << 5);
            if ((c & 0x1000) != 0) c ^= (RS_PRIMITIVE << 4);
            if ((c & 0x0800) != 0) c ^= (RS_PRIMITIVE << 3);
            if ((c & 0x0400) != 0) c ^= (RS_PRIMITIVE << 2);
            if ((c & 0x0200) != 0) c ^= (RS_PRIMITIVE << 1);
            if ((c & 0x0100) != 0) c ^= RS_PRIMITIVE;
            return c & 0xff;
        }

        /// <summary>
        /// Clears all potentially sensitive data stores.
        /// </summary>
        protected internal override void Reset()
        {
            if (_keySchedule != null)
            {
                Array.Clear(_keySchedule, 0, _keySchedule.Length);
            }
            base.Reset();
        }
    }
}